global descomprimir_archivo

;int descomprimir_archivo ( char *nomb_arch_entrada, char *nomb_arch_salida )

extern fclose
extern fopen
extern fread
extern fwrite
extern free
extern malloc
extern calloc
extern printf
extern cargar_tabla_codigo_desde_buffer
extern descomprimir_buffer


%define null 0
%define cod_ok 1
%define cod_not_ok 0
%define sizeof_header_oc2 16
%define sizeof_codificacion_t 9
%define max_cant_codigos 256
%define error_descomprimir_buffer -1


; defines header oc2
%define offset_tam_original 0
%define offset_long_codificacion 4
%define offset_cant_codigos 8
%define offset_offset_codificacion 12



section .data
formato_open_read:		db	'rb',0
formato_open_write:		db	'wb',0
msg_error_arch_ent_null:	db  'ERROR: No se permite que el nombre del archivo de entrada sea null',10,0
msg_error_arch_sal_null:	db  'ERROR: No se permite que el nombre del archivo de salida sea null',10,0
msg_error_abrir_arch_ent: 	db  'ERROR: No se pudo abrir el archivo %s',10,0
msg_error_abrir_arch_sal: 	db  'ERROR: No se pudo crear el archivo %s',10,0
msg_error_leer_header:		db  'ERROR: No se pudo leer el header',10,0
msg_error_cant_codigos_cero:	db  'ERROR: No se puede ser 0 la cantidad de codigos y el archivo no vacio',10,0
msg_error_cant_codigos_excedido: db  'ERROR: No pueden existir mas de 256 codigos diferentes',10,0
msg_error_sin_memoria:		db   'ERROR: No hay memoria suficiente',10,0
msg_error_cargar_tabla_codigos:	db   'ERROR: No se pudo cargar la tabla de codigos', 10,0
msg_error_fread:		db   'ERROR: Error al usar fread',10,0
msg_error_descomprimir_buffer:   db   'ERROR: No se puedo descomprimir el buffer',10,0

%define p_arch_ent ebp+8
%define p_arch_sal ebp+12
%define handle_in ebp-4
%define handle_out ebp-8
%define cod_error ebp-12
%define oc2_header ebp-28
%define p_tabla_codigos ebp-32
%define p_buffer ebp-36
%define p_buffer_out ebp-40

section .text

descomprimir_archivo:
	push ebp
	mov ebp,esp
	sub esp, 40
	
	push ebp
	push esi
	push edi
	
	mov dword [handle_in], null
	mov dword [handle_out], null
	mov dword [p_tabla_codigos], null
	mov dword [p_buffer], null
	mov dword [p_buffer_out], null
	mov dword [cod_error], cod_ok 	; asumimos al principio que esta todo ok.
	
	mov ebx, [p_arch_ent]
	cmp ebx, null
	jne _archivo_in_no_null
	
	push dword msg_error_arch_ent_null
	call printf
	add esp,4
	
	mov dword [cod_error], cod_not_ok
	jmp _liberar_recursos
	
_archivo_in_no_null:
	
	push dword formato_open_read
	push ebx
	call fopen
	add esp,8
	
	cmp eax, null
	jne _archivo_open_ok
	
	push dword [p_arch_ent]
	push dword msg_error_abrir_arch_ent
	call printf
	add esp,8
	
	mov dword [cod_error], cod_not_ok
	jmp _liberar_recursos
	
_archivo_open_ok:
	mov [handle_in], eax		; en eax tenemos el handle
	lea ebx, [oc2_header]		; en ebx tenemos el ptr al buffer para el header
	
	push eax
	push dword 1
	push dword sizeof_header_oc2
	push ebx
	call fread
	add esp,16
	
	cmp eax, 1			; si fread leyo bien debería devolver 1
	je _read_header_ok
	
	push dword msg_error_leer_header
	call printf
	add esp,4
	
	mov dword [cod_error], cod_not_ok
	jmp _liberar_recursos
	
_read_header_ok:
	mov eax, [ebx+offset_long_codificacion]
	cmp eax, 0
	jne _archivo_no_vacio_ent
	
	push dword 0
	push dword null				; le pasamos null para que lo genere vacio.
	push dword [p_arch_sal]
	call _volcar_archivo_sal		; generamos un archivo vacio.
	add esp,12
	
	mov [cod_error], eax
	jmp _liberar_recursos
	
_archivo_no_vacio_ent:
	mov esi, [ebx+offset_cant_codigos]	; en esi tenemos la cantidad de codigos
	cmp esi, 0
	jne _hay_codigos

	push dword msg_error_cant_codigos_cero
	call printf
	add esp,4
	
	mov dword [cod_error], cod_not_ok
	jmp _liberar_recursos
	
_hay_codigos:

	cmp esi, max_cant_codigos
	jng _cant_codigos_en_rango
	
	push dword msg_error_cant_codigos_excedido
	call printf
	add esp,4
	
	mov dword [cod_error], cod_not_ok
	jmp _liberar_recursos
	
_cant_codigos_en_rango:

	mov eax, esi
	mov edx, sizeof_codificacion_t
	mul edx
	
	mov edi, eax		; en edi tenemos lo que ocupa la tabla de codificacion_t
	
	push edi
	call malloc
	add esp,4
	
	cmp eax, null
	jne _llamar_a_cargar_tabla
	
	push dword msg_error_sin_memoria
	call printf
	add esp,4
	
	mov dword [cod_error], cod_not_ok
	jmp _liberar_recursos
	
_llamar_a_cargar_tabla:
	
	mov [p_buffer], eax	; guardamos el ptr al buffer para despues liberarlo.
	
	mov edx, [handle_in]
	push edx		; handler del archivo de entrada
	push edi		; edi tiene lo que ocupa la tabla de codificacion_t.
	push dword 1		; nmem = 1
	push eax		; eax tenía la memoria pedida por fread
	
	call fread
	add esp, 16
	
	
	lea edx, [p_tabla_codigos]
	push edx			; ptr doble a la tabla de codigos
	push edi			; size del buffer
	mov eax, [p_buffer]
	push eax			; ptr al buffer
	call cargar_tabla_codigo_desde_buffer
	add esp,12
	
	mov edi, [p_tabla_codigos]
	cmp edi, null

	jne _tabla_codigos_ok
	
	push dword msg_error_cargar_tabla_codigos
	call printf
	add esp,4
	
	mov dword [cod_error], cod_not_ok
	jmp _liberar_recursos
	
_tabla_codigos_ok:
	
	push dword [p_buffer]
	call free		; liberamos el buffer, porque necesitamos leer lo comprimido en el.
	add esp,4
	mov dword [p_buffer],null
	
	mov edi, [ebx+offset_long_codificacion]
	add edi, 96		; dado que esta en bits, para que no falte lugar.
	shr edi, 3		; pasamos a bytes.
	
	; en edi nos quedo la longitud en bytes en que entra la codificacion
	
	push edi
	push dword 1
	call calloc
	add esp,8
	
	cmp eax, null
	jne _leer_codificado
	
	push dword msg_error_sin_memoria
	call printf
	add esp,4
	
	mov dword [cod_error], cod_not_ok
	jmp _liberar_recursos	
	
_leer_codificado:

	mov esi, eax			; en esi dejamos el nuevo ptr al buffer
	mov [p_buffer], esi		; lo guardamos para liberar despues
	
	push dword [handle_in]		; handle archivo in no es null si llegamos aca.
	push edi			; size del bloque en bytes
	push dword 1			; 1 bloque solo
	push esi			; ptr al buffer
	
	; nota: le pasamos los parametros a fread nmem y el size invertido a proposito.
	; 	Porque esi tiene la cantidad de bytes por exceso en a lo supo 4 bytes.
	call fread
	add esp,16
	
	cmp eax,1			; Si fread leyo bien debería devolver 1
	jae _lectura_comprimido_ok
	
	push dword msg_error_fread
	call printf
	add esp,4
	
	mov dword [cod_error], cod_not_ok
	jmp _liberar_recursos
	
	
_lectura_comprimido_ok:	
	
	; preparamos el buffer de salida
	
	mov eax, [ebx+offset_tam_original]
	push eax
	push dword 1
	call calloc
	add esp,8
	
	cmp eax, null
	jne _buffer_out_ok
	
	push dword msg_error_sin_memoria
	call printf
	add esp,4
	
	mov dword [cod_error], cod_not_ok
	jmp _liberar_recursos	
	
	
_buffer_out_ok:
	
	mov [p_buffer_out], eax	        ; lo guardamos para liberarlo luego
	mov edx, [ebx+offset_tam_original]	; lo recargamos porque lo perdimos.
	
	mov ecx, [ebx+offset_long_codificacion] ; en ecx la longitud en bits de la codificacion.
	
	push dword [p_tabla_codigos]	; ptr a la tabla de codigos
	push edx			; size destino
	push eax			; ptr buffer destino
	push ecx			; long en bits de la codificacion
	push dword [p_buffer]		; ptr al buffer origen (el codificado)
	
	call descomprimir_buffer
	add esp,20
	
	cmp eax, error_descomprimir_buffer
	jne _volcar_resultado
	
	push dword msg_error_descomprimir_buffer
	call printf
	add esp,4
	
	mov dword [cod_error], cod_not_ok
	jmp _liberar_recursos
	
	
_volcar_resultado:	
	
	push dword [ebx+offset_tam_original]	; tamaño a escribir
	push dword [p_buffer_out]			; buffer de salida debería estar cargado.
	push dword [p_arch_sal]
	call _volcar_archivo_sal		; generamos un archivo vacio.
	add esp,12
	
	mov [cod_error], eax
	
	
_liberar_recursos:

	mov ebx, [p_tabla_codigos]
	cmp ebx, null
	je _liberar_buffer_out
	
	push ebx
	call free	; liberamos la memoria utilizada por la tabla de codigos.
	add esp,4
	
_liberar_buffer_out:

	mov ebx, [p_buffer_out]
	cmp ebx, null
	je _liberar_buffer_in
	
	push ebx
	call free	; liberamos la memoria usada por el buffer
	add esp,4
	
_liberar_buffer_in:

	mov ebx, [p_buffer]
	cmp ebx,null
	je _cerrar_archivos
	
	push ebx
	call free	; liberamos la memoria usada por el buffer
	add esp,4
	
_cerrar_archivos:
	
	mov ebx, [handle_in]
	cmp ebx,null
	je _validar_handle_out
	
	push ebx
	call fclose
	add esp,4
	
_validar_handle_out:
	
	mov ebx, [handle_out]
	cmp ebx, null
	je fin
	
	push ebx
	call fclose
	add esp,4
fin:
	mov eax, [cod_error]	; devolvemos el codigo de error actual

	pop edi
	pop esi
	pop ebp
	leave
	ret


;int _volcar_archivo_sal(char *archivo_salida, char *buffer, unsigned int size)
%define p_nombre_archivo_salida ebp+8
%define p_buffer_archivo_salida ebp+12
%define p_buffer_size_sal	ebp+16
_volcar_archivo_sal:
	push ebp
	mov ebp,esp
	push ebx
	push esi
	push edi
	
	mov ebx, cod_ok
	
	mov esi, [p_nombre_archivo_salida]
	cmp esi, null
	jne _nombre_arch_sal_no_null
	mov ebx, cod_not_ok
	jmp _fin_generar_archivo
	
_nombre_arch_sal_no_null:
	
	push dword formato_open_write
	push dword [p_nombre_archivo_salida]
	call fopen		; creamos o truncamos el archivo de salida
	add esp,8
	
	mov esi, eax		; en esi guardamos el handler para no perderlo
	
	cmp esi, null
	jne _write_salida
	mov ebx, cod_not_ok
	jmp _fin_generar_archivo
	
_write_salida:
	mov edi, [p_buffer_archivo_salida]
	cmp edi, null
	je _close_file_			; no se considera error recibir ptr null
	
	push esi			; handler
	push dword 1			; nmem = 1
	push dword [p_buffer_size_sal]	; size del buffer
	push edi			; ptr al buffer
	call fwrite
	add esp,16
	
	cmp eax, 1			; si fwrite logro escribir todo devuelve 1 (nmem era 1)
	je _close_file_
	
	mov ebx, cod_not_ok		; seteamos error
	
_close_file_:	
	push esi
	call fclose
	add esp,4
	
	
_fin_generar_archivo:

	mov eax, ebx	; devolvemos en eax el error	
	pop edi
	pop esi
	pop ebx
	leave
	ret
	
	
